Spring MVC 处理异步请求(下)- Spring 中的实例
本文承接上一篇对于 Callable 的详解,继续讲述 Spring 是如何使用 Callable,或者 DeferredResult 的返回结果,进行最终结果的处理的。因为上一篇已经叙述了 Callable 在线程池中的处理逻辑,本篇将直接基于 Spring MVC 的例子,来切实感受一下异步请求在实际应用中带来的便利。
使用 Callable 返回异步结果
首先,为了使我们的 MVC 工程可以接受异步请求的处理,我们需要在 web.xml 中为我们的 DispathcerServlet 加入以下的配置:
1 | <servlet> |
最重要的是最后一行配置:
其次,我们创建两个接口,分别用于执行阻塞的请求和不会阻塞的异步请求:
1 |
|
其中,blocking-request.do 将会阻塞,并且将会先执行 execute() 方法,然后再返回结果;而 non-blocking-request.do 将会直接返回后续的 println 中的内容,而在最后返回由其他线程处理的结果。
之后,我们需要创建一个 TaskService 抽象类,并且让我们的 DeferredService 类去实现。
1 | public abstract class TaskService { |
1 |
|
可以看到,DeferredService 会让线程阻塞一段时间(5秒),之后才会返回定向到的 jsp 页面名字。如果是使用阻塞的请求去执行该 service,则会让这个接口等在那里,并且不能接收其他的请求;而通过 Callable 执行异步请求的做法,将把这个耗时的逻辑转移到其他的线程,从而使接口可以及时接收其他的请求。
之后,我们运行 web 工程,分别输入:
localhost:8080/blocking-request.do: 该请求将直接进入阻塞状态,会在 TaskService 中等待 5 秒后,再将视图名称返回,因此,我们会看到 “task finished” 夹在中间,如下图所示:
localhost:8080/non-blocking-request.do: 该请求会将 Callable 对象中的逻辑放入其他线程处理,并且在 Controller 中表现为直接返回,并且在 Callable 有返回值时返回视图的结果。因此,”task finished” 会在最后,如下图所示:
从上面我们看出,Spring 在处理 Callable 的对象时,会将 Callable 转入其他线程处理,并且在 Controller 中直接返回使其可以处理其他的请求。而在 Callable 有结果返回时,再返回该异步请求的结果。
使用 DeferredResult 返回异步结果
根据 Spring MVC 文档,另一个选择就是返回 DeferredResult 对象。DeferredResult 比 Callable 更为复杂,但是也比 Callable 更为灵活,其体现在 DeferredResult 的返回值可以由任何一个线程产生,包括不由 Spring 控制的线程(对于 Callable 来说,返回还是在 Spring 的 WebAsyncMannager 实例中执行)。因此,我们可以在代码的其他线程内设置 DeferredResult 实例的具体返回值而不是像 Callable 中需要确切的结果返回(即 return result)。我们也提供一个实例来看看 DeferredResult 在真实的代码中是如何操作的。
我们在 DeferredController 中新增一个接口:
1 | "deffered-result.do", method = RequestMethod.GET) (value = |
这个接口将会向 ForkJoinPool (线程池的一个具体实现类) 提交一个 task,并且会在 sleep 过后去设置 DeferredResult 实例的结果。而这个 task 的处理被放入了其他线程,使得耗时的逻辑可以释放主线程资源,使得主线程可以接收新的请求。
之后,运行 web 工程,输入 http://localhost:8080/deffered-result.do,我们可以看到 console 输入的信息顺序如下:
显而易见,主线程已经完成 request 的处理,并且将 task 逻辑交给了其他线程,之后在其他线程处理完毕后,设置 DeferredResult 的结果。因此,我们可以看到 DeferredResult 的结果可以来自于其他线程。
小结
本章完成 Spring MVC 处理异步请求的实例代码讲解,让异步请求这个新特性对于刚刚接触 Spring 的我们来说,显得不再那么神秘。